﻿using System;
using SoftwareConsulting.BI11.NumericalMethods.Interfaces;
using SoftwareConsulting.BI11.NumericalMethods.DhbFunctionEvaluation;

namespace SoftwareConsulting.BI11.NumericalMethods.Iterations
{    
    /// <summary>
    /// Класс нахождения нулей функции с использованием приближения Ньютона
    /// </summary>
    public class NewtonZeroFinder : FunctionalIterator
    {        
        private IOneVariableFunction _derivativeFunction;//производная функции, где ищем нули

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="function"></param>
        /// <param name="start"></param>
        public NewtonZeroFinder(IOneVariableFunction function, double start) : 
            base(function)
        {
            StartingValue = start;
        }

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="function"></param>
        /// <param name="derivativeFunction"></param>
        /// <param name="start"></param>
        public NewtonZeroFinder(IOneVariableFunction function, IOneVariableFunction derivativeFunction, double start) 
            : this(function, start)
        {
            SetDerivative(derivativeFunction);
        }

        /// <summary>
        /// Выполняет итерацию
        /// </summary>
        /// <returns></returns>
        public override double DoIteration()
        {
            double delta = Function.GetValue(BestZeroApproximation) / _derivativeFunction.GetValue(BestZeroApproximation);
            SetBestZeroApproximation(BestZeroApproximation - delta);
            return GetRelativeError(Math.Abs(delta));
        }

        /// <summary>
        /// Инициализирует внутренние параметры
        /// </summary>
        public override void CreateRandomPopulation()
        {
            if (_derivativeFunction == null)
                _derivativeFunction = new FunctionDerivative(Function);
            if (double.IsNaN(BestZeroApproximation))
                SetBestZeroApproximation(0);
            int n = 0;
            Random random = new Random();
            while (MathExtended.Equal(_derivativeFunction.GetValue(BestZeroApproximation), 0))
            {
                if (++n > MaximumIterationsCount)
                    break;
                SetBestZeroApproximation(BestZeroApproximation + random.NextDouble());
            }
        }

        /// <summary>
        /// Возвращает производную функцию
        /// </summary>
        public IOneVariableFunction Derivative
        {
            get
            {
                return _derivativeFunction;
            }
        }

        /// <summary>
        /// Устанавливает производную функции, где ищем нули
        /// </summary>
        public void SetDerivative(IOneVariableFunction derivative)            
        {
            _derivativeFunction = new FunctionDerivative(Function);
            if (!MathExtended.Equal(_derivativeFunction.GetValue(BestZeroApproximation), derivative.GetValue(BestZeroApproximation), 0.001))
                throw new ArgumentException();//неточная производная
            _derivativeFunction = derivative;
        }        

        /// <summary>
        /// Возвращает/устанавливает функцию, где ищем нули
        /// </summary>
        public override IOneVariableFunction Function
        {
            get
            {
                return Function;
            }
            set
            {
                base.Function = value;
                _derivativeFunction = null;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает начальное значение поиска
        /// </summary>
        public double StartingValue
        {
            get
            {
                return BestZeroApproximation;
            }
            set 
            {
                SetBestZeroApproximation(value);
            }
        }
    }
}
